package com.gotokeep.keep.composition.demo.internal;

import android.graphics.SurfaceTexture;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMetadataRetriever;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import android.view.Surface;

import com.gotokeep.keep.composition.demo.renderer.Renderer;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;

/**
 * @author xana/cuixianming
 * @version 1.0
 * @since 2018-04-28 09:56
 */
public class SpeedPlayThread extends Thread {
    private static final String TAG = SpeedPlayThread.class.getSimpleName();
    private AtomicBoolean shouldRender;

    private MediaExtractor mediaExtractor;
    private MediaFormat audioFormat;
    private MediaCodec audioDecoder;
    private AudioTrack audioTrack;
    private MediaCodec.BufferInfo audioInfo = new MediaCodec.BufferInfo();
    private int audioTrackIndex = -1;
    private int sampleRate = 0;

    private int width = 0;
    private int height = 0;
    private int rotation = 0;
    private int frameRate = 0;
    private String filePath = "";


    private RenderListener renderListener;

    private boolean isVideoEnd = false;
    private float speed = 1.0f;

    public SpeedPlayThread(String filePath, AtomicBoolean shouldRender, float speed) {
        this.shouldRender = shouldRender;
        this.filePath = filePath;
        this.speed = speed;
    }

    private void initDecoder(String filePath) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            throw new RuntimeException("系统版本需要 4.3 以上");
        }
        File mediaFile = new File(filePath);
        if (!mediaFile.exists()) {
            throw new RuntimeException("文件不存在");
        }
        mediaExtractor = new MediaExtractor();
        try {
            mediaExtractor.setDataSource(filePath);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        audioTrackIndex = -1;
        for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
            MediaFormat format = mediaExtractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("audio/")) {
                audioTrackIndex = i;
                audioFormat = format;
                break;
            }
        }

        mediaExtractor.selectTrack(audioTrackIndex);
        sampleRate = audioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
        String mime = audioFormat.getString(MediaFormat.KEY_MIME);
        try {
            audioDecoder = MediaCodec.createDecoderByType(mime);
            int buffSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
            audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
                    AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT,
                    buffSize, AudioTrack.MODE_STREAM);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }


    private void init(String filePath) {
        initDecoder(filePath);
        audioDecoder.configure(audioFormat, null, null, 0);
        audioDecoder.start();
        audioTrack.play();
    }

    private void destroyDecoder() {

        audioDecoder.stop();
        audioDecoder.release();
        audioDecoder = null;

        audioTrack.stop();
        audioTrack.release();
        audioTrack = null;

        mediaExtractor.release();
        mediaExtractor = null;
    }

    private void release() {
        destroyDecoder();

    }

    private int getRotation(String path) {
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        try {
            retriever.setDataSource(path);
            String rotation = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
            return Integer.parseInt(rotation);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    private boolean playSound() {
        boolean decoded = false;
        while (!decoded) {
            int s = 0;
            int finalChunk[] = new int[0];
            int chunkSize = 0;
            int j = 0;
            while (s < speed) {
                int inputIndex = audioDecoder.dequeueInputBuffer(1000);
                if (inputIndex >= 0) {
                    ByteBuffer buffer = getInputBuffer(audioDecoder, inputIndex);
                    buffer.clear();
                    mediaExtractor.selectTrack(audioTrackIndex);
                    int sampleSize = mediaExtractor.readSampleData(buffer, 0);
                    if (sampleSize < 0) {
                        audioDecoder.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    } else {
                        audioDecoder.queueInputBuffer(inputIndex, 0, sampleSize, mediaExtractor.getSampleTime(),
                                mediaExtractor.getSampleFlags());
                        mediaExtractor.advance();
                    }
                }

                int outputIndex = audioDecoder.dequeueOutputBuffer(audioInfo, 1000);
                switch (outputIndex) {
                    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                        break;
                    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                        audioFormat = audioDecoder.getOutputFormat();
                        audioTrack.setPlaybackRate(audioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE));
                        break;
                    case MediaCodec.INFO_TRY_AGAIN_LATER:
                        break;
                    default:
                        ByteBuffer buffer = getOutputBuffer(audioDecoder, outputIndex);
                        final int[] chunk = new int[audioInfo.size / 4];
                        buffer.asIntBuffer().get(chunk);
                        buffer.clear();
                        if (finalChunk.length == 0) {
                            finalChunk = new int[audioInfo.size / 4];
                        }
                        int newSize = (int) (audioInfo.size / speed);
                        for (int i = 0; i < audioInfo.size / 4; i += speed) {
                            int sum = (int) (chunk[i] / speed);
                            for (int k = 1; k < speed; k++) {
                                if (i + k < audioInfo.size) sum += chunk[i + k] / speed;
                            }
                            if (j < newSize) {
                                finalChunk[j++] = sum;
                            }
                        }
                        audioDecoder.releaseOutputBuffer(outputIndex, false);
                        decoded = true;
                        break;
                }
                if (decoded) s += 1;
            }
            Log.d(TAG, "playSound: " + finalChunk.length);
            ByteBuffer finalBuffer = ByteBuffer.allocate(finalChunk.length * 4);
            finalBuffer.asIntBuffer().put(finalChunk);
            audioTrack.write(finalBuffer.array(), audioInfo.offset, audioInfo.offset + finalChunk.length);
        }
        return decoded;
    }

    private ByteBuffer getInputBuffer(MediaCodec decoder, int inputIndex) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return decoder.getInputBuffer(inputIndex);
        } else {
            return decoder.getInputBuffers()[inputIndex];
        }
    }

    private ByteBuffer getOutputBuffer(MediaCodec decoder, int outputIndex) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return decoder.getOutputBuffer(outputIndex);
        } else {
            return decoder.getOutputBuffers()[outputIndex];
        }
    }

    @Override
    public void run() {
        init(filePath);

        long startMs = SystemClock.elapsedRealtime();
        long lastTime = startMs;
        isVideoEnd = false;
        audioInfo.presentationTimeUs = 0;
        while (shouldRender != null && shouldRender.get()) {
            lastTime = SystemClock.elapsedRealtime();
            long playTime = lastTime - startMs;
            while (audioInfo.presentationTimeUs / 1000 <= playTime * speed) {
                playSound();
            }
        }
        if (renderListener != null) {
            renderListener.onVideoEnd();
        }
//        try {
//            Thread.sleep((long) ((videoInfo.presentationTimeUs / 1000 - lastTime) / speed));
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

        Log.d(TAG, "Render completed.");

        release();
    }

    public RenderListener getRenderListener() {
        return renderListener;
    }

    public void setRenderListener(RenderListener renderListener) {
        this.renderListener = renderListener;
    }

    public interface RenderListener {
        void onVideoSizeChanged(int width, int height, int rotation);
        void onVideoEnd();
    }
}
